package com.seanLab.tool.TagSuggestModel.TopicModel;

import com.google.gson.Gson;
import com.google.gson.JsonParser;
//import org.apache.spark.ml.clustering.LocalLDAModel;

import java.io.*;
import java.util.Random;

/**
 * LDA主题模型
 */
public class MyLDAModel {
    private static class TestMyLDAModel {
        public static void main(String[] args) throws IOException {
            JsonParser jsonParser = new JsonParser();
            MyLDAModel myLDAModel = null;
            try {
                myLDAModel = new Gson().fromJson(
                        new BufferedReader(new InputStreamReader(
                                new FileInputStream("LDAModel/MyLDAModel.json"))),
                        MyLDAModel.class);
                myLDAModel.loadVocabulary("vocabulary.dict");
                String content = "马来西亚羽球双打名将林钦华和炒鸡蛋，已经确定从9月开始，与韩国双打名将柳延星组成搭档，出战国际赛。28岁的前国手林钦华日前向媒体透露这项消息时证实，他和柳延星（31岁）合作的第一个比赛，是韩国公开赛。 　　林钦华与柳延星会擦出怎样的火花，令人非常期待，他俩也是继大马的陈文宏与印尼的亨德拉以后，另一对跨国男双羽球组合。 　　不同的是，陈文宏与亨德拉是大马和印尼，林钦华与柳延星是大马和韩国。林钦华出道以来，与多名好手组成拍档，包括麦喜俊、吴蔚升、陈炳顺、王建国和云天豪，也曾与黄惠龄、黄佩蒂、林芸如等打过混双，2015年正式离开国家队后，选择以自由人身分征战国际赛。  　　柳延星也是世界羽坛双打名将，既打混双也打男双，尤其与李龙大搭档，更是韩国男双一个时代的标杆；去年里约奥运会后，李龙大和柳延星先后退出国家队。级别为超级系列赛的韩国公开赛，将于9月12日至17日举行，总奖金高达60万美元，同样是超级系列赛的日本公开赛，将于19日至24日上演，总奖金有32万5000美元。打完韩国公开赛以后，“钦星”（清新）组合也会参加日本公开赛。  　　紫盟联赛撮合 　　林钦华坦承，类似异国搭档，最困难的是因为分隔两地，以致给2人的训练造成一定的困难，为此，他计划在9月初启程韩国与柳延星会合，以便在当地展开训练直至上场比赛，争取时间培养默契和磨合。 　　这次与柳延星搭档，林钦华本人将在著名羽球品牌apacs旗下参赛，他透露，与柳延星的“结盟”，源自于今年紫盟联赛。当时2人都代表蒲种联合队。  　　紫盟期间，他跟延星分享这个合作的心愿，得到正面回应，于是，两人在敲定以后就各自处理细节。此前，爱羽客小编也曾在一项业余赛事中也询问过林钦华退役后的打算，详情请戳：吴蔚昇前搭档自爆：在国家队压力很大 　　（注：林钦华，1989年，马来西亚男子羽毛球运动员；曾经搭档吴蔚昇在2014年大马首要超级赛上击败柴飚/洪炜，首次夺得超级系列赛冠军头衔。2015年2月，林钦华正式递交辞职信，申请离开马来西亚国家队。） 　　来源：中国报\n";
                double[] predict = myLDAModel.predictDoc(content);
                predict = myLDAModel.predictDoc(content);
                System.out.println(predict);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            System.out.println(myLDAModel.getK());
        }
    }

    private int K;
    private int vocabSize;
    private double topicConcentration;
    private double gammaShape;
    private double[] docConcentration;
    private double[][] topicsMatricx;
    private double[][] topicsMatricx_reverse;
    private Vocabulary vocabulary;

    public MyLDAModel() {
    }

//    public MyLDAModel(LocalLDAModel localLDAModel) {
//        this.K = localLDAModel.getK();
//        this.vocabSize = localLDAModel.vocabSize();
//        this.topicConcentration = localLDAModel.oldLocalModel().topicConcentration();
//        this.gammaShape = localLDAModel.oldLocalModel().gammaShape();
//        this.docConcentration = localLDAModel.oldLocalModel().docConcentration().toArray();
//        double[] topics = localLDAModel.oldLocalModel().topics().toArray();
//        topicsMatricx = new double[this.K][this.vocabSize];
//        for (int i = 0; i < this.K; i++) {
//            System.arraycopy(topics, i * this.vocabSize, topicsMatricx[i], 0, this.vocabSize);
//        }
//    }

    public void loadVocabulary(String path) throws IOException {
        vocabulary = new Vocabulary(path, vocabSize);
        topicsMatricx_reverse = new double[vocabSize][K];
        for (int i = 0; i < K; i++) {
            for (int j = 0; j < vocabSize; j++) {
                topicsMatricx_reverse[j][i] = topicsMatricx[i][j];
            }
        }
    }

    private int sample_word(int word, int count, int[] n_z, Random random) {
        double p[] = new double[n_z.length];
        double p_sum = 0d;
        for (int k = 0; k < n_z.length; k++) {
            p[k] = topicsMatricx_reverse[word][k] * (topicConcentration + n_z[k]);
            p_sum += p[k];
        }
        for (int k = 0; k < n_z.length; k++) {
            p[k] = p[k] / p_sum;
        }
        p_sum = random.nextDouble();
        int z = n_z.length - 1;
        for (int k = 0; k < n_z.length; k++) {
            p_sum -= p[k];
            if (p_sum < 0) {
                z = k;
                break;
            }
        }
        return z;
    }



    private boolean sample_doc(int[] doc, int[] n_z, int[] z, Random random) {
        boolean change = false;
        for (int i = 0; i < doc.length; i++) {

                int z_i = z[i];
                n_z[z_i] -= 1;
                int new_z = sample_word(i, doc[i], n_z, random);
                z[i] = new_z;
//                n_z[z_i] += 1 - doc[i];
                n_z[new_z] += 1;
                if (new_z != z_i) {
                    change = true;
                }

        }
        return change;
    }

    private boolean isZeros(int[] vector) {
        for (int i = 0; i < vector.length; i++) {
            if (vector[i] != 0) {
                return false;
            }
        }
        return true;
    }

    private boolean isZeros(double[] vector) {
        for (double x : vector) {
            if (x != 0) {
                return false;
            }
        }
        return true;
    }

    public double[] predictDoc(String doc) {
        return predictDoc(doc, 100);
    }

    public double[] predictDoc(String doc, int iter_max) {
        return predictDoc(vocabulary.transDoc2List(doc), iter_max);
    }

    public double[] predictDoc(int[] doc, int iter_max) {
//        System.out.println("start predict");
        long start = System.currentTimeMillis();
//        assert doc.length == vocabSize;
        double[] normalize = new double[this.K];
        if (doc.length < 1) {
            return normalize;
        }

        int k = this.K;
        int n = doc.length;
        int[] n_z = new int[k];
        int[] z = new int[n];
        Random random = new Random(System.currentTimeMillis());
        for (int i = 0; i < n; i++) {
//            z[i] = random.nextInt(k);
            z[i] = pickRandomTopic(random);
            n_z[z[i]] += 1;
        }
        for (int i = 0; i < iter_max; i++) {
            if (!sample_doc(doc, n_z, z, random)) {
//                System.out.println("break" + i);
                break;
            }
        }

        //normalize
        double max = 0d, min = Double.MAX_VALUE;
        for (int i = 0; i < k; i++) {
            if (n_z[i] < min) {
                min = n_z[i];
            }
            if (n_z[i] > max) {
                max = n_z[i];
            }
        }
        max = max - min;
        for (int i = 0; i < k; i++) {
            normalize[i] = ((double)n_z[i] - min) / max;
        }
        long end = System.currentTimeMillis();
//        System.out.println(end - start + "ms");
        if (isZeros(normalize)) {
            System.out.println("zeros!?");
        }
        return normalize;
    }

    public int pickRandomTopic(Random random) {
        double p_sum = random.nextDouble();
        int z = docConcentration.length - 1;
        for (int k = 0; k < docConcentration.length; k++) {
            p_sum -= docConcentration[k];
            if (p_sum < 0) {
                z = k;
                break;
            }
        }
        return z;
    }

    public int getK() {
        return K;
    }

    public void setK(int k) {
        K = k;
    }

    public int getVocabSize() {
        return vocabSize;
    }

    public void setVocabSize(int vocabSize) {
        this.vocabSize = vocabSize;
    }

    public double getTopicConcentration() {
        return topicConcentration;
    }

    public void setTopicConcentration(double topicConcentration) {
        this.topicConcentration = topicConcentration;
    }

    public double getGammaShape() {
        return gammaShape;
    }

    public void setGammaShape(double gammaShape) {
        this.gammaShape = gammaShape;
    }

    public double[] getDocConcentration() {
        return docConcentration;
    }

    public void setDocConcentration(double[] docConcentration) {
        this.docConcentration = docConcentration;
    }

    public double[][] getTopicsMatricx() {
        return topicsMatricx;
    }

    public void setTopicsMatricx(double[][] topicsMatricx) {
        this.topicsMatricx = topicsMatricx;
    }
}
